///PHILOTES Source Code.  (C)2012 PhiloLabs
#pragma once

#ifndef __EdgeListBuilder_H__
#define __EdgeListBuilder_H__

#include "OgrePrerequisites.h"
#include "math/vector4.h"
#include "OgreHardwareVertexBuffer.h"
#include "OgreRenderOperation.h"

namespace Ogre {
	
	


    
	class _OgreExport EdgeData : public MemAlloc_Geometry
    {
    public:
        
        struct Triangle {
            
            size_t indexSet; 
            
            size_t vertexSet;
            size_t vertIndex[3];/// Vertex indexes, relative to the original buffer
            size_t sharedVertIndex[3]; /// Vertex indexes, relative to a shared vertex buffer with 
                                        // duplicates eliminated (this buffer is not exposed)

			Triangle() :indexSet(0), vertexSet(0) {}
        };
        
        struct Edge {
            
            size_t triIndex[2];
            
            size_t vertIndex[2];
            
            size_t sharedVertIndex[2];
            
            bool degenerate;
        };

        // Array of 4D vector of triangle face normal, which is unit vector orthogonal
        // to the triangles, plus distance from origin.
        // Use aligned policy here because we are intended to use in SIMD optimised routines .
        //typedef std::vector<Vector4, STLAllocator<Vector4, CategorisedAlignAllocPolicy<Memory::ResourceHeap> > > TriangleFaceNormalList;
		typedef std::vector<Vector4, Memory::STLAllocator<Vector4, STL_Alloc_Geometry > > TriangleFaceNormalList;

        // Working vector used when calculating the silhouette.
        // Use std::vector<char> instead of std::vector<bool> which might implemented
        // similar bit-fields causing loss performance.
        typedef vector<char>::type TriangleLightFacingList;

        typedef vector<Triangle>::type TriangleList;
        typedef vector<Edge>::type EdgeList;

        
        struct EdgeGroup
        {
            
            size_t vertexSet;
            
            const VertexData* vertexData;
            
            size_t triStart;
            
            size_t triCount;
            
            EdgeList edges;

        };

        typedef vector<EdgeGroup>::type EdgeGroupList;
        
        TriangleList triangles;
        
        TriangleFaceNormalList triangleFaceNormals;
        
        TriangleLightFacingList triangleLightFacings;
        
        EdgeGroupList edgeGroups;
        
        bool isClosed;
        
        void updateTriangleLightFacing(const Vector4& lightPos);
        
        void updateFaceNormals(size_t vertexSet, const HardwareVertexBufferSharedPtr& positionBuffer);

        // Debugging method
		void log(Core::Log* log);
        
    };

    
    class _OgreExport EdgeListBuilder 
    {
    public:

        EdgeListBuilder();
        virtual ~EdgeListBuilder();
        
        void addVertexData(const VertexData* vertexData);
        
        void addIndexData(const IndexData* indexData, size_t vertexSet = 0, 
            RenderOperation::OperationType opType = RenderOperation::OT_TRIANGLE_LIST);

        
        EdgeData* build(void);

        /// Debugging method
		void log(Core::Log* l);
    protected:

        
        struct CommonVertex {
            Vector3  position;  // location of point in euclidean space
	        size_t index;       // place of vertex in common vertex list
            size_t vertexSet;   // The vertex set this came from
            size_t indexSet;    // The index set this was referenced (first) from
            size_t originalIndex; // place of vertex in original vertex set
        };
        
        struct Geometry {
            size_t vertexSet;           // The vertex data set this geometry data refers to
            size_t indexSet;            // The index data set this geometry data refers to
            const IndexData* indexData; // The index information which describes the triangles.
            RenderOperation::OperationType opType;  // The operation type used to render this geometry
        };
        
        struct geometryLess {
            bool operator()(const Geometry& a, const Geometry& b) const
            {
                if (a.vertexSet < b.vertexSet) return true;
                if (a.vertexSet > b.vertexSet) return false;
                return a.indexSet < b.indexSet;
            }
        };
        
        struct vectorLess {
            bool operator()(const Vector3& a, const Vector3& b) const
            {
                if (a.x < b.x) return true;
                if (a.x > b.x) return false;
                if (a.y < b.y) return true;
                if (a.y > b.y) return false;
                return a.z < b.z;
            }
        };

        typedef vector<const VertexData*>::type VertexDataList;
        typedef vector<Geometry>::type GeometryList;
        typedef vector<CommonVertex>::type CommonVertexList;

        GeometryList mGeometryList;
        VertexDataList mVertexDataList;
        CommonVertexList mVertices;
        EdgeData* mEdgeData;
		/// Map for identifying common vertices
		typedef map<Vector3, size_t, vectorLess>::type CommonVertexMap;
		CommonVertexMap mCommonVertexMap;
        
        typedef multimap< std::pair<size_t, size_t>, std::pair<size_t, size_t> >::type EdgeMap;
        EdgeMap mEdgeMap;

        void buildTrianglesEdges(const Geometry &geometry);

        /// Finds an existing common vertex, or inserts a new one
        size_t findOrCreateCommonVertex(const Vector3& vec, size_t vertexSet, 
            size_t indexSet, size_t originalIndex);
        /// Connect existing edge or create a new edge - utility method during building
        void connectOrCreateEdge(size_t vertexSet, size_t triangleIndex, size_t vertIndex0, size_t vertIndex1, 
            size_t sharedVertIndex0, size_t sharedVertIndex1);
    };
	
	

}
#endif

